/*
 * 项目名称:    wms-server-jeecg
 * 创建人员:    DongPeng
 * 创建时间:    2023-09-12
 * copyright(c) 2022 无锡星智数服科技有限公司
 */
package org.jeecg.modules.dock.config;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.jeecg.common.exception.JeecgBootException;

/**
 * Json工具
 *
 * @author DongPeng
 * @version 1.0.0
 * @date 2023-09-12
 * @description 类描述
 */
@UtilityClass
public final class JsonUtils {
    private final static ObjectMapper MAPPER = new ObjectMapper();

    static {
        //默认空值序列化
        MAPPER.setSerializerFactory(MAPPER.getSerializerFactory().withSerializerModifier(new NonNullBeanSerializerModifier()));
        MAPPER.setLocale(Locale.CHINA);
        MAPPER.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
        //这里使用skywalking 在设置所有字段都可见的情况下，会触发序列化异常
        MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.DEFAULT);
        //默认所有元素序列化
        MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        //忽略未知字段
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //Date格式不转化为timestamp时间戳格式
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //枚举类序列化时使用toString() 通过@JsonValue()定义
        //允许没有引号的字段名
        MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        //空对象序列化不失败
        MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        //时间序列化
        MAPPER.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //处理LocalDateTime
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
        //处理LocalDate
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN);
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
        //处理LocalTime
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN);
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
        //注册
        MAPPER.registerModule(javaTimeModule);
    }

    public ObjectMapper getInstance() {
        return MAPPER;
    }

    /**
     * 对象序列化
     *
     * @param object 对象
     * @return JSON字符串
     */
    @SneakyThrows
    public String toJson(@NonNull Object object) {
        return MAPPER.writeValueAsString(object);
    }

    /**
     * 美化输出
     *
     * @param object 对象
     * @return 格式化JSON字符串
     */
    @SneakyThrows
    public String toPrettyJson(@NonNull Object object) {
        return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
    }


    /**
     * 反序列化为对象
     *
     * @param json  json字符串
     * @param clazz 目标类Class
     * @param <T>   目标类
     * @return 目标对象
     */
    @SneakyThrows
    public <T> T toBean(@NonNull String json, @NonNull Class<T> clazz) {
        if (clazz.isAssignableFrom(Collection.class)) {
            return (T) toList(json, clazz);
        }
        return MAPPER.readValue(json, MAPPER.getTypeFactory().constructType(clazz));
    }

    /**
     * 反序列化为List
     *
     * @param json  json字符串
     * @param clazz 目标类Class
     * @param <T>   目标类
     * @return 目标对象集合
     */
    @SneakyThrows
    public <T> List<T> toList(@NonNull String json, @NonNull Class<T> clazz) {
        return MAPPER.readValue(json, MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz));
    }

    /**
     * 反序列化为Map
     *
     * @param json       json字符串
     * @param keyClass   key目标类Class
     * @param valueClass value目标类Class
     * @param <K>        key目标类
     * @param <V>        value目标类
     * @return Map对象
     */
    @SneakyThrows
    public <K, V> Map<K, V> toMap(@NonNull String json, @NonNull Class<K> keyClass, @NonNull Class<V> valueClass) {
        return MAPPER.readValue(json, MAPPER.getTypeFactory().constructMapType(HashMap.class, keyClass, valueClass));
    }

    /**
     * 读取JSON节点
     *
     * @param json json字符串
     * @return JSON节点
     */
    @SneakyThrows
    public JsonNode readJsonNode(String json) {
        return MAPPER.readTree(json);
    }


    public static class NonNullBeanSerializerModifier extends BeanSerializerModifier {
        @Override
        public List<BeanPropertyWriter> changeProperties(
                SerializationConfig config, BeanDescription beanDesc,
                List<BeanPropertyWriter> beanProperties) {
            // 循环所有的beanPropertyWriter
            beanProperties.forEach(writer -> {
                // 如果已经有 null 序列化处理如注解：@JsonSerialize(nullsUsing = xxx) 跳过
                if (writer.hasNullSerializer()) {
                    return;
                }

                //@JsonInclude 忽略
                JsonInclude jsonInclude = writer.getAnnotation(JsonInclude.class);
                if (jsonInclude != null) {
                    return;
                }
                JavaType type = writer.getType();
                Class<?> clazz = type.getRawClass();
                if (type.isTypeOrSubTypeOf(Character.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(String.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (type.isArrayType() || clazz.isArray() || type.isTypeOrSubTypeOf(Collection.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.ARRAY_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(Date.class) || type.isTypeOrSubTypeOf(TemporalAccessor.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (ClassUtil.isBasicType(clazz)) {
                    writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
                } else if (type.isTypeOrSubTypeOf(BigDecimal.class)) {
                    writer.assignNullSerializer(NullJsonSerializers.NULL_JSON_SERIALIZER);
                } else {
                    writer.assignNullSerializer(NullJsonSerializers.OBJECT_JSON_SERIALIZER);
                }
            });
            return super.changeProperties(config, beanDesc, beanProperties);
        }

        public interface NullJsonSerializers {

            JsonSerializer<Object> STRING_JSON_SERIALIZER = new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString(StrUtil.EMPTY);
                }
            };

            JsonSerializer<Object> ARRAY_JSON_SERIALIZER = new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeStartArray();
                    gen.writeEndArray();
                }
            };

            JsonSerializer<Object> OBJECT_JSON_SERIALIZER = new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeStartObject();
                    gen.writeEndObject();
                }
            };

            JsonSerializer<Object> NULL_JSON_SERIALIZER = new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeNull();
                }
            };

        }
    }
}

